package main

import (
	"encoding/json"
	"fmt"
	"github.com/bitly/go-simplejson"
	MQTT "github.com/eclipse/paho.mqtt.golang"
	"github.com/spf13/viper"
	"parking/src/globalvars"
	"parking/src/structs"
	"sync"
	"time"
)

//"mqtt": {
//"server": {
//"brokerAddress": "mqtt:\/\/47.101.183.85:1883\/",    gray-mqtt.feeleeiot.com:1883
//"userName": "controldevice",
//"password": "controldevice@1234"
//},
//"device": {
//"brokerAddress": "mqtt:\/\/192.168.20.22:1883\/",
//"userName": "feeyee",
//"password": "feeyee@1234"
//}
//},

var (
	reconnectAttemptsD int        // 重连计数器
	reconnectAttemptsS int        // 重连计数器
	maxReconnectRetry  = 5        // 最大重试次数
	reconnectMutex     sync.Mutex // 线程安全锁（可选）
)

var serverMqttClient MQTT.Client
var deviceMqttClient MQTT.Client
var serverMqttMsgChan = make(chan *structs.MQTTMessageStruct, 25)
var topicSubscribe_server = "/device/" + serialNumber + "/get"
var topicPublish_server = "/device/" + serialNumber + "/update"
var topicSubscribe_device = "/device/#"

func init() {
	LoadConfigInit(configPath, configName)
	go serverMqttClientInit()
	go deviceMqttClientInit()
}

func deviceMqttClientInit() {
	// 设置配置文件名
	viper.SetConfigName("config")
	// 设置配置文件类型
	viper.SetConfigType("json")
	// 添加配置文件路径
	viper.AddConfigPath(configPath)
	// 读取配置文件
	err := viper.ReadInConfig()
	if err != nil {
		fmt.Println("serverMqttClientInit :", err)
		return
	}
	// 解析配置
	brokerAddress := viper.GetString("config.mqtt.device.brokerAddress")
	username := viper.GetString("config.mqtt.device.username")
	password := viper.GetString("config.mqtt.device.password")
	heartbeat := viper.GetInt("config.mqtt.heartbeat")
	clientId := serialNumber

	// ------------------- 核心配置 -------------------
	opts := MQTT.NewClientOptions()
	opts.AddBroker(brokerAddress)
	opts.SetClientID(clientId)
	opts.SetUsername(username) // 如果需要，设置用户名
	opts.SetPassword(password) // 如果需要，设置密码

	opts.SetKeepAlive(time.Duration(heartbeat))    // 心跳检测间隔
	opts.SetAutoReconnect(true)                    // 启用自动重连
	opts.SetMaxReconnectInterval(10 * time.Second) // 最大重试间隔

	// ------------------- 事件注册 -------------------
	// 1. 连接丢失回调（必选）
	opts.SetConnectionLostHandler(func(client MQTT.Client, err error) {
		logger.Errorf("MQTT(Device)客户端掉线,clientId: %v", clientId)
		handleDeviceErrorShows(led485InfoList)
	})

	// 2. 重连中回调（可选）
	opts.SetReconnectingHandler(func(client MQTT.Client, opts *MQTT.ClientOptions) {
		reconnectMutex.Lock()
		defer reconnectMutex.Unlock()
		reconnectAttemptsD++
		logger.Infof("MQTT(Device)客户端正在重连,times: %v,brokerAddress: %v clientID: %v", reconnectAttemptsD, brokerAddress, clientId)
	})

	// 3. 连接成功回调（必选）
	opts.SetOnConnectHandler(func(client MQTT.Client) {
		logger.Infof("MQTT(Device)客户端连接成功,brokerAddress: %v clientID: %v", brokerAddress, clientId)
		// 重新订阅主题、恢复业务状态
		go deviceTopicSubscribeHandler(topicSubscribe_device, 0)
		reconnectAttemptsD = 0
	})

	// 客户端创建
	deviceMqttClient = MQTT.NewClient(opts)
	for {
		if token := deviceMqttClient.Connect(); token.Wait() && token.Error() != nil {
			logger.Error("连接 MQTT(Device) 代理失败:", token.Error())
			handleDeviceErrorShows(led485InfoList)
			time.Sleep(5 * time.Second)
		} else {
			break
		}
	}
}

// 向设备端阅主题并处理对应的业务
func deviceTopicSubscribeHandler(topic string, qos byte) {
	if token := deviceMqttClient.Subscribe(topic, qos, func(c MQTT.Client, m MQTT.Message) {
		// 网络在线状态
		online := globalvars.GetNetworkStatus()
		// 转化为Json,解析cmd字段,根据其处理不同业务
		payloadJson, err := simplejson.NewJson(m.Payload())
		if err != nil {
			logger.Errorf("解析设备端json消息失败,消息内容: %v", m.Payload())
			return
		}
		cmd, err := payloadJson.Get("cmd").String()
		if err != nil {
			logger.Errorf("解析设备端cmd类型失败,消息内容: %v", m.Payload())
			return
		}
		switch cmd {
		case "plateResult": //处理485显示
			if online {
				return
			}
			if offlineOpen == 1 {
				handleBarrierMessageInOffLineMod(payloadJson)
			}
		case "heartbeat":
			heartbeatChan <- payloadJson
		}

	}); token.Wait() && token.Error() != nil {
		logger.Errorf("MQTT(Device)订阅失败: %v，程序将在5s后重启", token.Error())
		time.Sleep(5 * time.Second)
		logger.Fatal()
	} else {
		logger.Infof("MQTT(Device)客户端订阅主题:%v", topic)
	}
}

func serverMqttClientInit() {
	// 设置配置文件名
	viper.SetConfigName("config")
	// 设置配置文件类型
	viper.SetConfigType("json")
	// 添加配置文件路径
	viper.AddConfigPath(configPath)
	// 读取配置文件
	err := viper.ReadInConfig()
	if err != nil {
		fmt.Println("serverMqttClientInit :", err)
		return
	}
	// 解析配置
	brokerAddress := viper.GetString("config.mqtt.server.brokerAddress")
	username := viper.GetString("config.mqtt.server.username")
	password := viper.GetString("config.mqtt.server.password")
	heartbeat := viper.GetInt("config.mqtt.heartbeat")
	clientId := serialNumber

	// ------------------- 核心配置 -------------------
	opts := MQTT.NewClientOptions()
	opts.AddBroker(brokerAddress)
	opts.SetClientID(clientId)
	opts.SetUsername(username) // 如果需要，设置用户名
	opts.SetPassword(password) // 如果需要，设置密码

	opts.SetKeepAlive(time.Duration(heartbeat))   // 心跳检测间隔
	opts.SetAutoReconnect(true)                   // 启用自动重连
	opts.SetMaxReconnectInterval(5 * time.Second) // 最大重试间隔

	// ------------------- 事件注册 -------------------
	// 1. 连接丢失回调（必选）
	opts.SetConnectionLostHandler(func(client MQTT.Client, err error) {
		logger.Errorf("MQTT客户端掉线,clientId: %v", clientId)
		// 网络状态切换 在线→离线
		globalvars.SetNetworkStatus(false)
		logger.Info("设备切换至离线模式")
		// 此处可添加告警通知（如邮件、短信）
		handleServerErrorShows(led485InfoList)
	})

	// 2. 重连中回调（可选）
	opts.SetReconnectingHandler(func(client MQTT.Client, opts *MQTT.ClientOptions) {
		reconnectMutex.Lock()
		defer reconnectMutex.Unlock()
		reconnectAttemptsS++
		logger.Infof("MQTT(server)客户端正在重连,times: %v,brokerAddress: %v clientID: %v", reconnectAttemptsS, brokerAddress, clientId)
	})

	// 3. 连接成功回调（必选）
	opts.SetOnConnectHandler(func(client MQTT.Client) {
		logger.Infof("MQTT(server)客户端连接成功,brokerAddress: %v clientID: %v", brokerAddress, clientId)
		// 重新订阅主题、恢复业务状态
		serverTopicSubscribeHandler(topicSubscribe_server, 0)
		// 请求配置
		mqttAccessServer("getConfig")
		// 网络状态切换 离线→在线
		globalvars.SetNetworkStatus(true)
		// 重连次数归零
		reconnectAttemptsS = 0
	})

	// 客户端创建
	serverMqttClient = MQTT.NewClient(opts)
	for {
		if token := serverMqttClient.Connect(); token.Wait() && token.Error() != nil {
			logger.Error("连接MQTT(server)代理失败:", token.Error())
			handleServerErrorShows(led485InfoList)
			time.Sleep(5 * time.Second)
		} else {
			break
		}
	}
}

// 向服务端订阅主题并处理对应的业务
func serverTopicSubscribeHandler(topic string, qos byte) {
	if token := serverMqttClient.Subscribe(topic, qos, func(c MQTT.Client, m MQTT.Message) {
		// 转化为Json,解析cmd字段,根据其处理不同业务
		payloadJson, err := simplejson.NewJson(m.Payload())
		if err != nil {
			logger.Errorf("解析服务端json消息失败,消息内容: %v", m.Payload())
			return
		}
		cmd, err := payloadJson.Get("cmd").String()
		if err != nil {
			logger.Errorf("解析服务端消息cmd类型失败,消息内容: %v", payloadJson)
			return
		}
		switch cmd {
		case "rs485Transmit": //处理485显示
			handleLed485Message(payloadJson)
		case "ioOutput": //处理开闸请求
			handleBarrierMessage(payloadJson)
		case "getConfigRsp": //接收并保存相关配置
			LocalStorageConnConfig(m.Payload(), configPath, configName)
			//配置重新加载
			ReloadConfig(configPath, configName)
		}
	}); token.Wait() && token.Error() != nil {
		logger.Errorf("MQTT(server)订阅失败: %v，程序将在5s后重启", token.Error())
		time.Sleep(5 * time.Second)
		logger.Fatal()
	} else {
		logger.Infof("MQTT(server)客户端订阅主题:%v", topic)
	}
}

// 向服务端发送请求抽象方法
func mqttAccessServer(cmd string) {
	accessServerStruct := structs.NewAccessServerStruct(cmd, serialNumber)
	payload, _ := json.Marshal(accessServerStruct)
	mqttMessage := structs.NewMQTTMessage(topicPublish_server, 0, false, payload)
	mqttSendMessageToServer(mqttMessage)
}

// 向服务端发送消息函数
func mqttSendMessageToServer(mqttMessage *structs.MQTTMessageStruct) {
	//todo 日志优化
	if token := serverMqttClient.Publish(mqttMessage.GetTopic(), mqttMessage.GetQos(), mqttMessage.GetRetained(), mqttMessage.GetPayload()); token.Wait() && token.Error() != nil {
		logger.Errorf("发布消息失败,topic:%v qos:%v", mqttMessage.GetTopic(), mqttMessage.GetQos())
	} else {
		logger.Debugf("消息发布成功,topic:%v qos:%v", mqttMessage.GetTopic(), mqttMessage.GetQos())
	}
}
